﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Transactions;
using System.Xml;
using System.Xml.Linq;
using System.ComponentModel;
using AIS2.DataAccess;
using MassIntellect;
using MassIntellect.WorkIt.Frames;
using MassIntellect.WorkIt;
using System.Data.Linq;

namespace AIS2.Frames
{
	public class TBCMessageReader : ServiceDependedFrame<IDAServiceTBCMsg>
	{
		[Serialize]
		public string MessageType { get; set; }

		[Serialize]
		[Description("Количество обрабатываемых сообщений в течении сессии. После фрейм деактивируется. При -1 не останавливается.")]
		public int InputWindow { get; set; }

		[Serialize]
		[Description("Количество одновременно обрабатываемых сообщений.")]
		public int ChainLength { get; set; }

		[Serialize]
		public string ReaderName { get; set; }

		[Serialize]
		public string ResultKey { get; set; }

		[Serialize(IgnoreDefault = true)]
		public string ContentKey { get; set; }

		[Serialize(IgnoreDefault = true)]
		public string CodeOPKey { get; set; }

		[Serialize(IgnoreDefault = true)]
		public string ReportIDKey { get; set; }

		[Serialize(IgnoreDefault = true)]
		public string SenderKey { get; set; }

		[Serialize(IgnoreDefault = true)]
		public string SubjectKey { get; set; }

		[Serialize(IgnoreDefault = true)]
		public Guid SelectLoad { get; set; }

		[Serialize(SerializeAs.None)]
		public int MessageCounter { get; set; }

		[Serialize]
		public bool IsAutoDeactivateIfEmptyInbox { get; set; }

		public TBCMessageReader()
		{
			InputWindow = -1;
		}

		protected override void protectedPulse(PulseArgs args)
		{
			Echo("TBCMessageSystem checking box...");
			if (GetService() != null)
			{
				ReadNewMessages(args);
				ThroughPulse(args);
			}
			else
				ThrowException(new Exception("Ошибка доступа к базе."));
		}

		private IPipe ResultLoad { get; set; }

		public override IPipe this[string nameIndex]
		{
			get
			{
				return nameIndex == "ResultLoad"
					? ResultLoad
					?? (ResultLoad = new PipeLog
						{
							Handler = args =>
										{
#if !DEBUG
											try
#endif
											{
												XElement xe = args[GetKeyOrDefault(ResultKey, "Result")] as XElement;
												Guid regId = (Guid)args[GetKeyOrDefault(SubjectKey, "RegistrationID")];
												if (xe != null)
												{
													switch ((string)xe.Element("Result"))
													{
														case "OK":
															Log("Отмечаем отчёт Message.RegistrationID '{0}' как успешно обработанный.", regId);
															WriteLog(regId, "Отчет загружен.");
															break;
														default:
															Log("Отмечаем отчёт Message.RegistrationID '{0}' как обработанный с ошибками.", regId);
															WriteLog(regId, "Отчет загружен с ошибками.");
															break;
													}
												}
												else
												{

													Log("Отмечаем отчёт Message.RegistrationID '{0}' как обработанный с ошибками.", regId);
													WriteLog(regId, "Отчет загружен с ошибками.");
												}
											}
#if !DEBUG
											catch (Exception ex)
											{
												Log("Ошибка обработки потока сообщений {0}. {1}", args.Id, ex.Message);
											}
#endif
										}
						})
					: base[nameIndex];
			}
		}

		public static Regex CodeOPFromEMail = new Regex(@"([a-z0-9\.\-_]*)(?=@)", RegexOptions.Compiled | RegexOptions.IgnoreCase);

		private readonly List<DataAccess.Message> buffer = new List<DataAccess.Message>();

		private void ReadNewMessages(PulseArgs args)
		{
			Match m;
			if (InputWindow != 0)
			{
				if (buffer.Count == 0)
				{
					var readLength = Math.Max(Math.Min(InputWindow, ChainLength), 1);
					if (SelectLoad == Guid.Empty)
						buffer.AddRange(readNewMessages().Select(s => s.InnerMessage).Take(readLength));
					else
					{
						buffer.Add(ReadMessage(SelectLoad));
						SelectLoad = Guid.Empty;
					}
				}
				var ida = ServiceLocator.Resolve<IDAService>();
				var readed = false;
				Report rep;
				if (buffer.Count > 0)
				{
					var msg = buffer[0];
					buffer.RemoveAt(0);
					if (InputWindow > 0)
						InputWindow--;
					MessageCounter++;
					Guid? messageID = new Guid(msg.MessageID);
					Log("Сообщение № {0}", MessageCounter);
					if (msg != null)
#if (!DEBUG)
                    try
#endif
					{
						Log("======== Messages.RegistrationID {0}", msg.RegistrationID);
						//using (var ts = new TransactionScope())
						{
							//проверяем на дубль по Messages.RegistrationID, если находим, то удаляем отчёт по его ReportID
							using (var db = ida.GetContext())
							{

								foreach (var dub in db.Reports.Where(s => s.IsNotActive.HasValue && !s.IsNotActive.Value &&  s.PPVID == messageID).ToArray())
								{
									Log("ReportID '{0}' MessageID '{1}' RegistrationID '{2}' need to be deleted ! ", dub.ReportID, dub.PPVID,
									    dub.MessageID);
#if !DEBUG
									try
#endif
									{
										db.DeleteReport(dub.ReportID);
									}
#if !DEBUG
                                    catch (Exception ex)
                                    {
                                        Log("Ошибка при удалении отчёта ReportID '{0}'! Exception: {1}".goFormat(dub.ReportID, ex.Message));
                                    }
#endif
								}
							}
							m = CodeOPFromEMail.Match(msg.MessageSource);
							if (!m.Success)
								Log("Не удалось распознать код ОП в строке {0}".goFormat(msg.MessageSource));

							//создаем новый отчёт
							using (var db = ida.GetContext())
							{
								rep = new Report
								      	{
								      		ReportID = Guid.NewGuid(),
											IsNotActive= false,
								      		CrDate = DateTime.Now,
								      		ReportSource = msg.MessageSource,
								      		ReportDate = msg.MessageDate.HasValue ? msg.MessageDate.Value : msg.RegistrationDateTime,
								      		CodeOP = m.Success ? m.Value.GetSubstring(50) : "",
								      		MessageID = msg.RegistrationID,
								      		PPVID = messageID
								      	};
								db.Reports.InsertOnSubmit(rep);
								Log("Загрузка отчёта: RegID:{0} -> RepID:{1}", msg.RegistrationID, rep.ReportID);
								//Echo("Чтение {0}...", item.RegistrationID);
								readed = true;
#if !DEBUG
                                try
#endif
								{
									db.SubmitChanges(ConflictMode.FailOnFirstConflict);
								}
#if !DEBUG
                                catch (Exception ex)
                                {
                                    Log("Ошибка сохранения отчета в базе: {0}", ex.Message);
                                }
#endif
							}

							// передача принятого пакета
							var pulse = args.Clone().AddParams(
								PulsePrm.Create("RegistrationID", msg.RegistrationID),
								PulsePrm.Create(GetKeyOrDefault(SenderKey, "Sender"), msg.MessageSource),
								PulsePrm.Create(GetKeyOrDefault(SubjectKey, "Subject"), messageID),
								PulsePrm.Create(GetKeyOrDefault(ContentKey, "Content"), msg.MessageXmlData),
								PulsePrm.Create(GetKeyOrDefault(ReportIDKey, "ReportID"), rep.ReportID),
								PulsePrm.Create(GetKeyOrDefault(CodeOPKey, "CodeOP"), rep.CodeOP)
								);
							ThroughPulse(pulse);

							//ts.Complete();

						}

						//using (var ts_log = new TransactionScope())

					}
#if !DEBUG
					catch (Exception ex)
					{
						ThrowException(ex);
					}
#endif
				}
				if (!readed)
				{
					Echo("TBCMessageSystem new message not found.");
					if (IsAutoDeactivateIfEmptyInbox)
						IsActive = false;
				}
			}
			if (InputWindow == 0)
			{
				Echo("InputWindow is elapsed.");
				IsActive = false;
			}

		}

		private DataAccess.Message ReadMessage(Guid selectLoad)
		{
			using (var db = Service.GetContext())
			{
				return db.ReadMessage(selectLoad).FirstOrDefault();
			}
		}

		private IEnumerable<Message> readNewMessages()
		{
			using (var db = Service.GetContext())
			{
				var msgs = db.ReadNewMessages(ReaderName, MessageType);
				if (msgs != null)
					foreach (var item in msgs)
						yield return new Message { InnerMessage = item, Reader = this };
			}
		}

		private DataAccess.Message readNewMessage()
		{
			using (var db = Service.GetContext())
			{
				return db.ReadNewMessages(ReaderName, MessageType).FirstOrDefault();
			}
		}

		public void WriteLog(Guid msgRegID, string textEvent)
		{
			using (var db = GetService().GetContext())
			{
				int? res = 0;
				db.sproc_ReadersLogInsert(Guid.NewGuid(), ReaderName, msgRegID, DateTime.Now, textEvent, ref res);
			}
		}

		public class Message
		{
			public DataAccess.Message InnerMessage;
			public TBCMessageReader Reader;
			public void MakeAsReaded()
			{
				Reader.WriteLog(InnerMessage.RegistrationID, "Message Readed");
			}
		}
	}

	public class PipeLog : Pipe
	{
		public Action<PulseArgs> Handler { get; set; }

		public override void Pulse(PulseArgs args)
		{
			if (Handler != null)
				Handler(args);
		}
	}
}
